package dao

import (
	"context"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)

var ErrWaitingSMSNotFound = gorm.ErrRecordNotFound

type AsyncSmsDAO interface{
	Insert(ctx context.Context, s AsyncSms) error
	GetWaitngSMS(ctx context.Context) (AsyncSms, error)
	MarkSuccess(ctx context.Context, id int64) error
	MarkFailed(ctx context.Context, id int64) error
}

const (
	asyncStatusWaiting = iota
	asyncStatusFailed
	asyncStatusSuccess
)

type GormAsyncSmsDAO struct {
	db *gorm.DB
}

func NewAsyncSmsDAO (db *gorm.DB) AsyncSmsDAO {
	return &GormAsyncSmsDAO{
		db: db,
	}
}

func (g GormAsyncSmsDAO) Insert(ctx context.Context, s AsyncSms) error {
	return g.db.WithContext(ctx).Create(&s).Error
}

func (g GormAsyncSmsDAO) GetWaitngSMS(ctx context.Context) (AsyncSms, error) {
	// 高并发情况下，select for update对数据库压力很大
	var s AsyncSms
	err:=g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 只找1分钟前的异步短信发送
		now:=time.Now().UnixMilli()
		endTime:=now-time.Minute.Milliseconds()
		// select xx from xxx where xx for update
		err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("update_time < ? and status = ? ", endTime, asyncStatusWaiting).
			First(&s).Error
		if err!=nil {
			return err
		}
		err = tx.Model(&AsyncSms{}).Where("id=?",s.Id).Updates(map[string]any{
			"retry_cnt": gorm.Expr("retry_cnt+1"),
			"update_time": now,
		}).Error
		return err
	})
	return s, err
}

func (g GormAsyncSmsDAO) MarkSuccess(ctx context.Context, id int64) error {
	now:=time.Now().UnixMilli()
	return g.db.WithContext(ctx).Model(&AsyncSms{}).
		Where("id=?", id).
		Updates(map[string]any{
			"update_time": now,
			"status": asyncStatusSuccess,
		}).Error
}

func (g GormAsyncSmsDAO) MarkFailed(ctx context.Context, id int64) error {
	now:=time.Now().UnixMilli()
	return g.db.WithContext(ctx).Model(&AsyncSms{}).
		Where("id=? and `retry_cnt`>=`retry_max`", id).
		Updates(map[string]any{
			"update_time": now,
			"status": asyncStatusFailed,
	}).Error
}

type AsyncSms struct {
	Id int64
	//Config sqlx.JsonColumn[SmsConfig]
	RetryCnt int
	RetryMax int
}